package com.caishi.lkx.order.api;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.caishi.lkx.common.BaseApi;
import com.caishi.lkx.common.ienum.DeviceType;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.common.unionpay.sdk.AcpService;
import com.caishi.lkx.common.unionpay.sdk.SDKConstants;
import com.caishi.lkx.common.unionpay.sdk.SDKUtil;
import com.caishi.lkx.distribution.mapper.DistributionWithdrawalsMapper;
import com.caishi.lkx.distribution.model.DistributionWithdrawals;
import com.caishi.lkx.distribution.properties.WeixinModel;

import com.caishi.lkx.order.pay.UnionPayPlugins;
import com.caishi.lkx.user.model.DistributionUser;
import com.caishi.lkx.user.service.IDistributionUserService;
import com.caishi.lkx.user.service.IGroupService;
import com.caishi.lkx.utils.*;
import com.zzw.common.Wrappers;
import com.zzw.common.annotation.ApiLogIgnore;
import com.zzw.common.annotation.ResultIgnore;

import com.caishi.lkx.order.ienum.status.OrderStatus;
import com.caishi.lkx.order.ienum.status.PaymentStatus;
import com.caishi.lkx.order.ienum.type.PaymentType;
import com.caishi.lkx.order.model.OrderModel;
import com.caishi.lkx.order.model.PaymentModel;
import com.caishi.lkx.order.pay.WeiXinPayPlugins;
import com.caishi.lkx.order.pay.ZFBPayPlugins;
import com.caishi.lkx.order.service.IOrderService;
import com.caishi.lkx.order.service.IPaymentService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/order/pay")
@Slf4j
public class PayApi extends BaseApi {

    @Resource
    private IOrderService orderService;

    @Resource
    private IPaymentService paymentService;
    private final IDistributionUserService distributionUserService;

    @Resource
    private AcpService acpService;
    private final WeixinModel weixinModel;
    private final TransferToUser transferToUser;

    @Resource
    private IGroupService groupService;
    private final DistributionWithdrawalsMapper distributionWithdrawalsMapper;

    @Autowired
    public PayApi(WeixinModel weixinModel, TransferToUser transferToUser, IDistributionUserService distributionUserService, DistributionWithdrawalsMapper distributionWithdrawalsMapper) {
        this.weixinModel = weixinModel;
        this.transferToUser = transferToUser;
        this.distributionUserService = distributionUserService;
        this.distributionWithdrawalsMapper = distributionWithdrawalsMapper;
    }


    @Operation(summary = "支付宝支付完成回调")
    @PostMapping("/alipay-success-notify")
    @Roles
    @ApiLogIgnore(false)
    @ResultIgnore
    @Transactional(rollbackFor = Exception.class)
    public Object alipayPaySuccess(
            @RequestParam(value = "out_trade_no") String outTradeNo,
            @RequestParam(value = "trade_no") String tradeNo,
            @RequestParam(value = "trade_status") String tradeStatus,
            @RequestParam Map<String, String> param
    ) {
        log.info("支付宝回调：{},{},{},{}", outTradeNo, tradeNo, tradeStatus, param);
        if ("TRADE_SUCCESS".equals(tradeStatus)) {
            // 检验签名
            payNotify(outTradeNo, param.get("trade_no"), param);
            return true;
        }
        return false;

    }

    // AcpService.java 和 Acp6Service.java
    // EncryptionKerayHandlerMethodArgumentResolverConfig对Request进行了处理
    @Operation(summary = "微信回调")
    //
    @PostMapping(value = "/weixin-success-notify", produces = MediaType.TEXT_XML_VALUE, consumes = MediaType.TEXT_XML_VALUE)
    @Roles
    @ApiLogIgnore(false)
    @ResultIgnore
    @Transactional(rollbackFor = Exception.class)
    public String weixinPaySuccess(@RequestBody Map<String, String> param) {
        log.info("支付成功回调{}", param);
        payNotify(param.get("out_trade_no"), param.get("transaction_id"), param);
        return "<xml>\n" +
                "\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>";
    }

    @Operation(summary = "银联支付成功回调")
    @PostMapping(value = "/unionpay-success-notify", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    @Roles
    @ApiLogIgnore(false)
    @ResultIgnore
    @Transactional(rollbackFor = Exception.class)
    public String unionPaySuccess(@RequestParam Map<String, String> param) {

        log.info("支付成功回调{}", param);

        String orderId = param.get("counterNo");
        var order = orderService.getById(orderId);
        if (order == null) {
            log.info("获取订单信息失败{}", orderId);
            return "FAILED";
        }
        //获取对应机构
        var group = groupService.getById(order.getGroupId());
        if (group == null) {
            log.info("获取机构信息失败，订单id{}", orderId);
            return "FAILED";
        }
        if (!SDKUtil.verifySha256(param, group.getUnionPayConfig().get("secureKey").toString(), SDKConstants.UTF_8_ENCODING, true)) {
            log.info("验证签名结果[失败].");
            return "FAILED";
        } else {
            log.info("验证签名结果[成功].");
            payNotify(orderId, param.get("seqId"), param);

        }
        return "SUCCESS";
    }

    @Operation(summary = "苹果支付完成回调")
    @PostMapping("/apply-success-notify")
    @Roles
    @ApiLogIgnore(false)
    @ResultIgnore
    @Transactional(rollbackFor = Exception.class)
    public Object applyPaySuccess(
            @RequestParam Map<String, String> param
    ) {
        log.info("苹果回调：{}", param);
        // 检验签名
        payNotify(param.get("order_id"), param.get("order_id"), param);
        return true;
    }

    public void payNotify(String orderId, String serialNumber, Map<String, String> image) {
        OrderModel orderModel = orderService.getById(orderId);
        paymentService.update(null, Wrappers.<PaymentModel>lambdaUpdate().eq(PaymentModel::getOrderId, orderId).set(PaymentModel::getSerialNumber, serialNumber));
        if (orderModel.getOrderStatus() == OrderStatus.waitPay) {
            paymentService.paySuccess(orderModel.getId(), image, orderModel.getRecommendId());
        } else {
            PaymentModel paymentModel = paymentService.getPayModel(orderModel.getId());
            if (paymentModel.getPaymentStatus() != PaymentStatus.paySuccess) {
                paymentService.paySuccess(orderModel.getId(), image, orderModel.getRecommendId());
            }
        }
        paymentService.paySuccess(orderModel.getId(), image, orderModel.getRecommendId());
    }


    @Operation(summary = "获取三方支付签名")
    @GetMapping("/sign")
    public Map<String, Object> paySign(@RequestParam PaymentType type,
                                       @RequestParam String orderId,
                                       @RequestParam(required = false, defaultValue = "unknown") String aType,
                                       @RequestParam(defaultValue = "unknown") DeviceType paymentChannel,
                                       @RequestParam(required = false, defaultValue = "") String code,
                                       @RequestParam(required = false, defaultValue = "") String openid
    ) {
        log.info("发起支付,{},{}", type.getDesc(), aType);
        Map<String, Object> otherParams = MapUtil.<String, Object>builder()
                .build();
        otherParams.put("ip", userContext.currentIp());
        otherParams.put("openid", openid);
        otherParams.put("code", code);
        if (type == PaymentType.alipay) {
            return paymentService.getPaySign(userContext.currentUserId(), type, orderId, paymentChannel, ZFBPayPlugins.AlipayType.valueOf(aType), otherParams);
        } else if (type == PaymentType.weixin) {
            return paymentService.getPaySign(userContext.currentUserId(), type, orderId, paymentChannel, WeiXinPayPlugins.WeixinPayType.valueOf(aType), otherParams);
        } else if (type == PaymentType.unionpay) {
            return paymentService.getPaySign(userContext.currentUserId(), type, orderId, paymentChannel, UnionPayPlugins.UnionPayType.valueOf(aType), otherParams);
        }
        return paymentService.getPaySign(userContext.currentUserId(), type, orderId, paymentChannel, null, otherParams);
    }

    //  微信转账成功回调,可能有探测请求，可能有重复请求。商户系统必须能够正确处理重复的通知。如果已处理过，直接给微信支付返回成功。
//    验签通过：商户需告知微信支付接收回调成功，HTTP应答状态码需返回200或204，无需返回应答报文。
//
//    验签不通过：商户需告知微信支付接收回调失败，HTTP应答状态码需返回5XX或4XX，同时需返回以下应答报文
    @PostMapping("/weixin-transfer-success-notify")
    public void weixinTransferSuccess( HttpServletRequest request,HttpServletResponse  response) throws Exception {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, String> headerMap = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            headerMap.put(headerName, headerValue);
        }
        // 超出5秒，放弃
        if(Long.parseLong(headerMap.get("Wechatpay-Timestamp"))*1000-System.currentTimeMillis()>5000L){
            response.setStatus(500);
            return;
        }


        // 平台证书切换到公私钥期间，回调要同时支持使用平台证书和公钥的验签。
        // RSACombinedNotificationConfig作为 NotificationConfig来处理回调
        // 验签是用请求体字符串与请求头的签名进行算法比较验证
        // 回调的时间戳是秒级的
        // 校验探测请求。即探测签名是错误签名，应正常进行校验，这是为了防止不做处理直接返回等情况
        // Wechatpay-Serial对应平台证书序列号

//        NotificationConfig config = new RSACombinedNotificationConfig.Builder()
//                .merchantId(merchantId)
//                .privateKeyFromPath(privateKeyPath)
//                .merchantSerialNumber(merchantSerialNumber)
//                .publicKeyFromPath(wechatpayPublicKeyPath)
//                .publicKeyId(wechatpayPublicKeyId)
//                .apiV3Key(apiV3Key)
//                .build();

//        NotificationConfig config = new RSAPublicKeyNotificationConfig.Builder()
//                .publicKeyFromPath(publicKeyPath)
//                .publicKeyId(publicKeyId)
//                .apiV3Key(apiV3Key)
//                .build();


//        PublicKey wechatPayPublicKey = null; //微信支付公钥
//        String plaintext = "";
//        PrivacyEncryptor encryptor = new RSAPrivacyEncryptor(wechatPayPublicKey);
//        String ciphertext = encryptor.encryptToString(plaintext);

        String requestBody = RequestReader.getRequestBody(request);
        String message = String.format("%s\n%s\n%s\n", headerMap.get("Wechatpay-Timestamp"), headerMap.get("Wechatpay-Nonce"),
                requestBody);
        boolean passFlag=false;
        CertificateType certificateType;

        if(headerMap.get("Wechatpay-Serial").equals(weixinModel.getPaySerialNumber())){
            certificateType=CertificateType.PUBLIC_KEY;
            passFlag = WXPayUtility.verify(message, headerMap.get("Wechatpay-Signature"), "SHA256withRSA", transferToUser.getWechatPayPublicKey());
        }else{
            // 不是公钥就是平台证书
            if(headerMap.get("Wechatpay-Serial").equals(weixinModel.getPlatformId())){
                certificateType=CertificateType.PLATFORM;
                passFlag=WechatPaySignatureVerifier.verifySignature(message, headerMap.get("Wechatpay-Signature"), transferToUser.getPlatformKey());
            }else{
                // 都没有，不正常，返回500
                response.setStatus(500);
                return;
            }
        }
        if(passFlag){
            // 提现表数据入库
            // 解密请求体
            AesUtil aesUtil = new AesUtil(weixinModel.getApiV3().getBytes(StandardCharsets.UTF_8));
            JSONObject resultObject = JSONObject.parseObject(requestBody);
            JSONObject resource = resultObject.getJSONObject("resource");
            String ciphertext = resource.getString("ciphertext");
            String associatedData = resource.getString("associated_data");
            String nonce = resource.getString("nonce");


            String jsonStr = aesUtil.decryptToString(associatedData.getBytes(), nonce.getBytes(), ciphertext);
            // 查询库中用没有转账订单id，有则为重复请求，不做处理返回200
            JSONObject jsonObject = JSONObject.parseObject(jsonStr);
            QueryWrapper<DistributionWithdrawals> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("transfer_bill_no",jsonObject.getString("transfer_bill_no"));
            boolean exists = distributionWithdrawalsMapper.exists(queryWrapper);
            if( exists){
                response.setStatus(200);
                return;
            }

            // 不存在则插入入库
            if(DistributionWithdrawals.StatusType.valueOf(jsonObject.getString("state"))==DistributionWithdrawals.StatusType.SUCCESS){
                QueryWrapper<DistributionUser> queryWrapper1 = new QueryWrapper<>();
                queryWrapper1.eq("openid",jsonObject.getString("openid"));
                DistributionUser one = distributionUserService.getOne(queryWrapper1);
                DistributionWithdrawals distributionWithdrawals = DistributionWithdrawals.builder()
                        .id(jsonObject.getLong("id"))
                        // 设置user_id，防止多openid切换不好找user
                        .userId(one.getId())
                        .amount(jsonObject.getLong("transfer_amount"))
//                        .bankInfo(jsonObject.getString("bank_info"))
                        .withdrawalDate(LocalDateTime.now())
                        .remark(jsonObject.getString("fail_reason"))
                        .openid(jsonObject.getString("openid"))
                        .transferBillNo(jsonObject.getString("transfer_bill_no"))
                        .createdAt(LocalDateTime.now())
                        .status(DistributionWithdrawals.StatusType.valueOf(jsonObject.getString("state")))
                        .build();
                distributionWithdrawalsMapper.insert(distributionWithdrawals);
            }
            response.setStatus(200);
        }else{
            response.setStatus(500);
        }


    }
    public static enum CertificateType {
         PLATFORM,// 平台证书
         PUBLIC_KEY;//支付公钥
    }
}
